﻿using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.UI;
using ARchGL.Platform.Authorization.Permissions;
using ARchGL.Platform.Authorization.Permissions.Dto;
using ARchGL.Platform.Authorization.Roles.Dto;
using ARchGL.Platform.Utils;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace ARchGL.Platform.Authorization.Roles
{
    /// <summary>
    /// Application service that is used by 'role management' page.
    /// </summary>
#if !DEBUG
    [AbpAuthorize(AppPermissions.Pages_Administration_Roles)]
#endif
    public class RoleAppService : PlatformAppServiceBase, IRoleAppService
    {
        private readonly RoleManager _roleManager;

        public RoleAppService(RoleManager roleManager)
        {
            _roleManager = roleManager;
        }

        /// <summary>
        /// 获取角色-分页数据集合
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<RoleListDto>> GetRoles(GetRolesInput input)
        {
            var roles = _roleManager
                .Roles
                .WhereIf(!input.Permission.IsNullOrWhiteSpace(),
                    r => r.Permissions.Any(rp => rp.Name == input.Permission && rp.IsGranted))
                .WhereIf(!input.Filter.IsNullOrWhiteSpace(), x =>
                    x.Name.Contains(input.Filter) ||
                    x.DisplayName.Contains(input.Filter) ||
                    x.Description.Contains(input.Filter));

            var result = await roles.OrderBy(x => x.Id).PageByAsync(input);

            return new PagedResultDto<RoleListDto>(roles.Count(),
                ObjectMapper.Map<List<RoleListDto>>(result));
        }

        /// <summary>
        /// 根据Id集合获取角色
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public async Task<List<RoleListDto>> QueryRolesByIdList(long[] list)
        {
            var result = await _roleManager.Roles.Where(x => list.Contains(x.Id)).ToListAsync();
            return ObjectMapper.Map<List<RoleListDto>>(result);
        }

        /// <summary>
        /// 根据 角色编码 集合获取角色
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public async Task<List<RoleListDto>> QueryRolesByCodeList(string[] list)
        {
            var result = await _roleManager.Roles.Where(x => list.Contains(x.Name)).ToListAsync();
            return ObjectMapper.Map<List<RoleListDto>>(result);
        }

#if !DEBUG
        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Create, AppPermissions.Pages_Administration_Roles_Edit)]
#endif
        public async Task<GetRoleForEditOutput> GetRoleForEdit(NullableIdDto input)
        {
            var permissions = PermissionManager.GetAllPermissions();
            var grantedPermissions = new Permission[0];
            RoleEditDto roleEditDto;

            if (input.Id.HasValue) //Editing existing role?
            {
                var role = await _roleManager.GetRoleByIdAsync(input.Id.Value);
                grantedPermissions = (await _roleManager.GetGrantedPermissionsAsync(role)).ToArray();
                roleEditDto = ObjectMapper.Map<RoleEditDto>(role);
            }
            else
            {
                roleEditDto = new RoleEditDto();
            }

            return new GetRoleForEditOutput
            {
                Role = roleEditDto,
                Permissions = ObjectMapper.Map<List<FlatPermissionDto>>(permissions).OrderBy(p => p.Name).ToList(),
                GrantedPermissionNames = grantedPermissions.Select(p => p.Name).ToList()
            };
        }

        /// <summary>
        /// 创建或修改角色
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task CreateOrUpdateRole(CreateOrUpdateRoleInput input)
        {
            if (input.Role.Id.HasValue)
            {
                await UpdateRoleAsync(input);
            }
            else
            {
                await CreateRoleAsync(input);
            }
        }

        /// <summary>
        /// 删除角色
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
#if !DEBUG
        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Delete)]
#endif
        public async Task DeleteRole(EntityDto input)
        {
            var role = await _roleManager.GetRoleByIdAsync(input.Id);
            await DeleteRoleUser(role);
        }

        /// <summary>
        /// 批量删除角色
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
#if !DEBUG
        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Delete)]
#endif
        public async Task BatchDeleteRole(List<int> input)
        {
            foreach (var id in input)
            {
                var role = await _roleManager.GetRoleByIdAsync(id);
                await DeleteRoleUser(role);
            }
        }

#if !DEBUG
        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Edit)]
#endif
        protected virtual async Task UpdateRoleAsync(CreateOrUpdateRoleInput input)
        {
            Debug.Assert(input.Role.Id != null, "input.Role.Id should be set.");

            var role = await _roleManager.GetRoleByIdAsync(input.Role.Id.Value);
            role.DisplayName = input.Role.DisplayName;
            role.Description = input.Role.Description;
            role.IsDefault = input.Role.IsDefault;

            await UpdateGrantedPermissionsAsync(role, input.GrantedPermissionNames);
        }

#if !DEBUG
        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Create)]
#endif
        protected virtual async Task CreateRoleAsync(CreateOrUpdateRoleInput input)
        {
            var role = new Role(AbpSession.TenantId, input.Role.DisplayName) { IsDefault = input.Role.IsDefault };
            role.Description = input.Role.Description;
            CheckErrors(await _roleManager.CreateAsync(role));
            await CurrentUnitOfWork.SaveChangesAsync(); //It's done to get Id of the role.
            await UpdateGrantedPermissionsAsync(role, input.GrantedPermissionNames);
        }

        #region Private Method

        private async Task UpdateGrantedPermissionsAsync(Role role, List<string> grantedPermissionNames)
        {
            var grantedPermissions = PermissionManager.GetPermissionsFromNamesByValidating(grantedPermissionNames);
            await _roleManager.SetGrantedPermissionsAsync(role, grantedPermissions);
        }

        private async Task DeleteRoleUser(Role role)
        {
            StaticRoleNames.CheckIsSystemInfo(role.Name);

            if (role.Name.Contains(StaticRoleNames.Tenants.System)) throw new UserFriendlyException(L("StaticRole_Tooltip"));
            var users = await UserManager.GetUsersInRoleAsync(role.Name);
            foreach (var user in users)
            {
                CheckErrors(await UserManager.RemoveFromRoleAsync(user, role.Name));
            }

            CheckErrors(await _roleManager.DeleteAsync(role));
        }

        #endregion
    }
}
